package controllers;

import java.util.Vector;

import models.*;
import views.*;

public class TurretTrackingSystem {
	
	//these are to be input from the main interface at a later stage
	public static final Point3D tower_2 = new Point3D(2,0,0);
	public static final Point3D tower_3 = new Point3D(0,2,0);
	public static final Point3D tower_4 = new Point3D(0,0,2);
	
	public static RoomAngle turret_current_angle;
	
	/**
	 * The reading at position 0 is the most recent one.
	 */
	private static Vector<Point3D> last_ten_readings = new Vector<Point3D>(10);
	public static int readings_done_up_to_ten = 0;
	
	public static final double threshold = 0.1; //threshold for "good readings", a higher value allows for larger deviations
	public static final int millisPerReading = 200;
	public static final int millisPerWriting = 200;
	
	public static final String version = "0.2 alpha";
	
	public static boolean turretOnline = false;
	public static boolean hasGoodReading = false;
	
	public static TurretControlpanel controlpanel;
	public static OutputConsole statusConsole;
	public static OutputConsole talkativeConsole;
	
	public static Thread readerThread = null;
	public static Thread writerThread;

	public static void main(String[] args) throws InterruptedException {
		
		controlpanel = new TurretControlpanel();
		statusConsole = new OutputConsole("Turret Status & Action",1);
		controlpanel.disableToggleButton();
		statusConsole.addString("Calibrating, please wait...");
		Thread zeroPositioning = new Thread(new TurretZeroPositionThread());
		zeroPositioning.start();
		zeroPositioning.join();
		statusConsole.addString("Calibrating complete!");
		controlpanel.enableToggleButton();
		
	}
	
	/**
	 * This method toggles the status of the turret, including the appropriate startup and/or termination of threads.
	 * @throws InterruptedException 
	 * @throws InterruptedException 
	 */
	public static void toggleTurret() throws InterruptedException {
		turretOnline = !turretOnline;
		if(readerThread != null){
			statusConsole.addString("Stopping readings...");
			disposeOfReaderThread();
		}
		if(writerThread != null){
			statusConsole.addString("Stopping writings...");
			disposeOfWriterThread();
		}
		if(talkativeConsole != null) {
			disposeOfTalkativeConsole();
		}
		if(turretOnline) {
			talkativeConsole = new OutputConsole("Data Overview",2);
			readerThread = new Thread(new ReaderThread());
			statusConsole.addString("Starting readings...");
			readerThread.start();
			writerThread = new Thread(new WriterThread());
			statusConsole.addString("Starting writings...");
			writerThread.start();
			statusConsole.addString("Turret online!");
		}
		else {
			readings_done_up_to_ten = 0;
			last_ten_readings.clear();
			hasGoodReading = false;
			controlpanel.disableToggleButton();
			statusConsole.addString("Returning to zero, please wait...");
			Thread zeroPositioning = new Thread(new TurretZeroPositionThread());
			zeroPositioning.start();
			zeroPositioning.join();
			statusConsole.addString("Return complete.");
			controlpanel.enableToggleButton();
			statusConsole.addString("Turret offline!");
		}
	}
	
	public static void disposeOfWriterThread() {
		try {
			if(writerThread.isAlive())
				writerThread.join(millisPerWriting*3);
			if(writerThread.isAlive()){
				writerThread.interrupt();
				writerThread.join();
			}
		} catch (InterruptedException e) {
			
		}
		writerThread = null;
	}
	
	public static void disposeOfReaderThread() {
		try {
			if(readerThread.isAlive())
				readerThread.join(millisPerReading*3);
			if(readerThread.isAlive()){
				readerThread.interrupt();
				readerThread.join();
			}
		} catch (InterruptedException e) {
			
		}
		readerThread = null;
	}
	
	public static void disposeOfStatusConsole() {
		statusConsole.dispose();
		statusConsole = null;
	}
	
	public static void disposeOfTalkativeConsole() {
		talkativeConsole.dispose();
		talkativeConsole = null;
	}
	
	public static synchronized Point3D addOrReadReading(boolean add, double da, double db, double dc, double dd) {
		if(add) {
			last_ten_readings.add(0,PositioningCalculations.calculatePosition(
				tower_2, tower_3, tower_4,
				da, db, dc, dd));
			if(readings_done_up_to_ten < 10)
				readings_done_up_to_ten++;
			return null;
		}
		else {
			if(readings_done_up_to_ten > 0) {
				Point3D newMedian = getMedianOfReadings();
				double distance_to_target = PositioningCalculations.calculateDistanceBetweenTwoPoints(
						new Point3D(0,0,0),
						last_ten_readings.get(0));
				double distance_between_target_and_median = PositioningCalculations.calculateDistanceBetweenTwoPoints(
						newMedian, last_ten_readings.get(0));
				if(Math.abs(distance_to_target / distance_between_target_and_median) > threshold)
					hasGoodReading = false;
				else {
					hasGoodReading = true;
				}
				return newMedian;
			}
			else {
				hasGoodReading = false;
				return null;
			}
		}
	}
	
	private static Point3D getMedianOfReadings() {
		double 	total_x = 0,
				total_y = 0,
				total_z = 0;
		for(int i = 0; i < readings_done_up_to_ten; i++) {
			Point3D reading = last_ten_readings.get(i);
			total_x = total_x + reading.x;
			total_y = total_y + reading.y;
			total_z = total_z + reading.z;
		}
		return new Point3D(
				total_x/readings_done_up_to_ten,
				total_y/readings_done_up_to_ten,
				total_z/readings_done_up_to_ten);
	}

}
